﻿namespace UnityEditor.Graphs.AnimationStateMachine
{
    using NUnit.Framework;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using UnityEditor.Graphs;
    using UnityEditorInternal;
    using UnityEngine;

    internal class Graph : UnityEditor.Graphs.Graph
    {
        [NonSerialized]
        private StateMachine m_ActiveStateMachine;
        [NonSerialized]
        private AnyStateNode m_AnyStateNode;
        private readonly Dictionary<string, EdgeInfo> m_ConnectedSlotsCache = new Dictionary<string, EdgeInfo>();
        private UnityEditorInternal.State m_DefaultState;
        private int m_StateCount;
        private int m_StateMachineCount;
        private readonly Dictionary<StateMachine, StateMachineNode> m_StateMachineNodeLookup = new Dictionary<StateMachine, StateMachineNode>();
        private readonly Dictionary<UnityEditorInternal.State, StateNode> m_StateNodeLookup = new Dictionary<UnityEditorInternal.State, StateNode>();
        private int m_TransitionCount;
        [NonSerialized]
        public StateMachine parentStateMachine;
        [NonSerialized]
        public StateMachine rootStateMachine;

        public void BuildGraphFromStateMachine(StateMachine stateMachine)
        {
            Assert.IsNotNull(stateMachine);
            Assert.IsNotNull(this.rootStateMachine);
            this.Clear();
            this.m_ActiveStateMachine = stateMachine;
            this.CreateNodes();
            this.CreateEdges();
            this.m_StateCount = this.m_ActiveStateMachine.GetStateCount();
            this.m_StateMachineCount = this.m_ActiveStateMachine.GetStateMachineCount();
            this.m_TransitionCount = this.rootStateMachine.transitionCount;
            this.m_DefaultState = this.rootStateMachine.DefaultState();
        }

        private T CreateAndAddNode<T>(string name, Vector3 position) where T: UnityEditor.Graphs.AnimationStateMachine.Node
        {
            T node = ScriptableObject.CreateInstance<T>();
            node.hideFlags = HideFlags.HideAndDontSave;
            node.name = name;
            node.position = new Rect(position.x, position.y, 0f, 0f);
            node.AddInputSlot("In");
            node.AddOutputSlot("Out");
            this.AddNode(node);
            return node;
        }

        private void CreateAnyStateNode()
        {
            this.m_AnyStateNode = this.CreateAndAddNode<AnyStateNode>("Any State", this.activeStateMachine.anyStatePosition);
            this.m_AnyStateNode.color = UnityEditor.Graphs.Styles.Color.Aqua;
        }

        private void CreateEdges()
        {
            this.m_ConnectedSlotsCache.Clear();
            foreach (Transition transition in this.rootStateMachine.transitions)
            {
                UnityEditorInternal.State srcState = transition.GetSrcState();
                UnityEditorInternal.State dstState = transition.GetDstState();
                if ((srcState != null) || (dstState.GetStateMachine() == this.activeStateMachine))
                {
                    UnityEditor.Graphs.AnimationStateMachine.Node srcNode = this.FindNode(srcState);
                    UnityEditor.Graphs.AnimationStateMachine.Node dstNode = this.FindNode(dstState);
                    string key = GenerateConnectionKey(srcNode, dstNode);
                    if (this.m_ConnectedSlotsCache.ContainsKey(key))
                    {
                        this.m_ConnectedSlotsCache[key].transitions.Add(transition);
                    }
                    else if (srcNode != dstNode)
                    {
                        Slot fromSlot = srcNode.outputSlots.First<Slot>();
                        Slot toSlot = dstNode.inputSlots.First<Slot>();
                        this.Connect(fromSlot, toSlot);
                        this.m_ConnectedSlotsCache.Add(key, new EdgeInfo(transition));
                    }
                }
            }
        }

        private void CreateNodeFromState(UnityEditorInternal.State state)
        {
            StateNode node = this.CreateAndAddNode<StateNode>(string.Empty, state.GetPosition());
            node.state = state;
            if (this.rootStateMachine.DefaultState() == state)
            {
                node.color = UnityEditor.Graphs.Styles.Color.Orange;
            }
            this.m_StateNodeLookup.Add(state, node);
        }

        private void CreateNodeFromStateMachine(StateMachine subStateMachine)
        {
            StateMachineNode node = this.CreateAndAddNode<StateMachineNode>(string.Empty, this.activeStateMachine.GetStateMachinePosition(subStateMachine));
            node.stateMachine = subStateMachine;
            node.style = "node hex";
            if (subStateMachine.HasState(this.rootStateMachine.DefaultState()))
            {
                node.color = UnityEditor.Graphs.Styles.Color.Orange;
            }
            this.m_StateMachineNodeLookup.Add(subStateMachine, node);
        }

        private void CreateNodes()
        {
            this.m_StateNodeLookup.Clear();
            this.m_StateMachineNodeLookup.Clear();
            IEnumerator<UnityEditorInternal.State> enumerator = this.activeStateMachine.states.GetEnumerator();
            try
            {
                while (enumerator.MoveNext())
                {
                    UnityEditorInternal.State current = enumerator.Current;
                    this.CreateNodeFromState(current);
                }
            }
            finally
            {
                if (enumerator == null)
                {
                }
                enumerator.Dispose();
            }
            IEnumerator<StateMachine> enumerator2 = this.activeStateMachine.stateMachines.GetEnumerator();
            try
            {
                while (enumerator2.MoveNext())
                {
                    StateMachine subStateMachine = enumerator2.Current;
                    this.CreateNodeFromStateMachine(subStateMachine);
                }
            }
            finally
            {
                if (enumerator2 == null)
                {
                }
                enumerator2.Dispose();
            }
            this.CreateAnyStateNode();
            if (this.parentStateMachine != null)
            {
                this.CreateParentStateMachineNode();
            }
        }

        private void CreateParentStateMachineNode()
        {
            StateMachineNode node = this.CreateAndAddNode<StateMachineNode>("(Up) ", this.activeStateMachine.parentStateMachinePosition);
            node.stateMachine = this.parentStateMachine;
            node.style = "node hex";
            if (this.parentStateMachine.HasState(this.rootStateMachine.DefaultState()) && !this.activeStateMachine.HasState(this.rootStateMachine.DefaultState()))
            {
                node.color = UnityEditor.Graphs.Styles.Color.Orange;
            }
            this.m_StateMachineNodeLookup.Add(this.parentStateMachine, node);
        }

        public bool DisplayDirty()
        {
            return ((this.activeStateMachine.GetStateCount() != this.m_StateCount) || ((this.activeStateMachine.GetStateMachineCount() != this.m_StateMachineCount) || ((this.rootStateMachine.transitionCount != this.m_TransitionCount) || (this.rootStateMachine.DefaultState() != this.m_DefaultState))));
        }

        public UnityEditor.Graphs.AnimationStateMachine.Node FindNode(UnityEditorInternal.State state)
        {
            if (state == null)
            {
                return this.m_AnyStateNode;
            }
            if (this.m_StateNodeLookup.ContainsKey(state))
            {
                return this.m_StateNodeLookup[state];
            }
            IEnumerator<StateMachine> enumerator = this.activeStateMachine.stateMachines.GetEnumerator();
            try
            {
                while (enumerator.MoveNext())
                {
                    StateMachine current = enumerator.Current;
                    if (current.HasState(state))
                    {
                        return this.m_StateMachineNodeLookup[current];
                    }
                }
            }
            finally
            {
                if (enumerator == null)
                {
                }
                enumerator.Dispose();
            }
            return this.m_StateMachineNodeLookup[this.parentStateMachine];
        }

        public UnityEditor.Graphs.AnimationStateMachine.Node FindNode(StateMachine stateMachine)
        {
            if (stateMachine == null)
            {
                return null;
            }
            if (stateMachine == this.activeStateMachine)
            {
                return null;
            }
            if (this.m_StateMachineNodeLookup.ContainsKey(stateMachine))
            {
                return this.m_StateMachineNodeLookup[stateMachine];
            }
            IEnumerator<StateMachine> enumerator = this.activeStateMachine.stateMachines.GetEnumerator();
            try
            {
                while (enumerator.MoveNext())
                {
                    StateMachine current = enumerator.Current;
                    if (current.HasStateMachine(stateMachine))
                    {
                        return this.m_StateMachineNodeLookup[current];
                    }
                }
            }
            finally
            {
                if (enumerator == null)
                {
                }
                enumerator.Dispose();
            }
            return this.m_StateMachineNodeLookup[this.parentStateMachine];
        }

        private static string GenerateConnectionKey(UnityEditor.Graphs.Node srcNode, UnityEditor.Graphs.Node dstNode)
        {
            return (srcNode.GetInstanceID() + "->" + dstNode.GetInstanceID());
        }

        public EdgeInfo GetEdgeInfo(Edge edge)
        {
            if (edge.toSlot == null)
            {
                return null;
            }
            return this.m_ConnectedSlotsCache[GenerateConnectionKey(edge.fromSlot.node, edge.toSlot.node)];
        }

        internal override UnityEditor.Graphs.GraphGUI GetEditor()
        {
            UnityEditor.Graphs.AnimationStateMachine.GraphGUI hgui = ScriptableObject.CreateInstance<UnityEditor.Graphs.AnimationStateMachine.GraphGUI>();
            hgui.graph = this;
            hgui.hideFlags = HideFlags.HideAndDontSave;
            return hgui;
        }

        private void ReadAnyStatePosition(AnyStateNode anyStateNode)
        {
            anyStateNode.position.x = this.activeStateMachine.anyStatePosition.x;
            anyStateNode.position.y = this.activeStateMachine.anyStatePosition.y;
        }

        public void ReadNodePositions()
        {
            foreach (UnityEditor.Graphs.AnimationStateMachine.Node node in base.nodes)
            {
                if (node is StateNode)
                {
                    ReadStatePosition(node as StateNode);
                }
                if (node is StateMachineNode)
                {
                    this.ReadStateMachinePosition(node as StateMachineNode);
                }
                if (node is AnyStateNode)
                {
                    this.ReadAnyStatePosition(node as AnyStateNode);
                }
            }
        }

        private void ReadStateMachinePosition(StateMachineNode stateMachineNode)
        {
            Vector2 parentStateMachinePosition;
            if (stateMachineNode.stateMachine == this.parentStateMachine)
            {
                parentStateMachinePosition = this.activeStateMachine.parentStateMachinePosition;
            }
            else
            {
                parentStateMachinePosition = this.activeStateMachine.GetStateMachinePosition(stateMachineNode.stateMachine);
            }
            stateMachineNode.position.x = parentStateMachinePosition.x;
            stateMachineNode.position.y = parentStateMachinePosition.y;
        }

        private static void ReadStatePosition(StateNode stateNode)
        {
            Vector2 position = stateNode.state.GetPosition();
            stateNode.position.x = position.x;
            stateNode.position.y = position.y;
        }

        public void RebuildGraph()
        {
            this.BuildGraphFromStateMachine(this.activeStateMachine);
        }

        public StateMachine activeStateMachine
        {
            get
            {
                return this.m_ActiveStateMachine;
            }
            set
            {
                if (this.m_ActiveStateMachine != value)
                {
                    this.BuildGraphFromStateMachine(value);
                }
            }
        }
    }
}

